package top.superflow.flow.runtime.process

import java.lang.reflect.Method
import java.util.{Map => JavaMap}

import scala.collection.JavaConverters._
import scala.collection.mutable.Map


object RequestMatchAsst {
  
  
  def isRequestMatch(method : Method, originalRequest : Object) : Boolean = {
    if(isNatureMatch(method, originalRequest)){
      return true
    }
    
    if(containsParamAsObject(method, originalRequest)){
      return true
    }
    
    containsParamAsMap(method, originalRequest)
  }
  
  def converRequest(method : Method, originalRequest : Object) : Map[String, Object] = {
    if(isNatureMatch(method, originalRequest)){
      return convertRequestFromNature(method, originalRequest)
    }
    
    if(containsParamAsObject(method, originalRequest)){
      return convertRequestFromObject(method, originalRequest)
    }
    
    if(containsParamAsMap(method, originalRequest)){
      return convertRequestFromMap(method, originalRequest)
    }
    
    generateEmptyParams(method)
  }
  
  private def generateEmptyParams(method : Method) : Map[String, Object] = {
    val emptyParams = Map[String, Object]()
    
    for(param <- method.getParameters){
      emptyParams += (param.getName -> null)
    }
    
    emptyParams
  }
  
  private def convertRequestFromNature(method : Method, originalRequest : Object) : Map[String, Object] = {
    val convertedParams = Map[String, Object]()
    val paramCount = method.getParameterCount
    
    if(paramCount == 0){
      return convertedParams;
    }
    
    if(paramCount == 1){
      convertedParams += (method.getParameters.apply(0).getName -> originalRequest)
      return convertedParams
    }
    
    val paramArray = originalRequest.asInstanceOf[Array[_]]
    for(i <- 0 to paramCount - 1){
      val param = method.getParameters.apply(i)
      convertedParams += (param.getName -> paramArray.apply(i).asInstanceOf[Object])
    }
    
    convertedParams
  }
  
  private def convertRequestFromObject(method : Method, originalRequest : Object) : Map[String, Object] = {
    val convertedParams = Map[String, Object]()
    val fields = originalRequest.getClass.getDeclaredFields
    
    for(field <- fields){
      convertedParams += (field.getName -> field.get(originalRequest))
    }
    
    convertedParams
  }
  
  private def convertAcceptableParamMap(originalRequest : JavaMap[_,_]) : Map[String, Object] = {
    val convertedParams = Map[String, Object]()
    for((paramKey , paramValue)  <- originalRequest.asScala){
      val paramType = getParamType(paramValue)
      convertedParams += (paramKey.toString() -> paramValue.asInstanceOf[Object])
    }
    convertedParams
  }
  
  private def convertRequestFromMap(method : Method, originalRequest : Object) : Map[String, Object] = {
    val paramMap = originalRequest.asInstanceOf[JavaMap[_,_]]
    
    convertAcceptableParamMap(paramMap)
  }
  
  
  
  private def containsParamAsMap(method : Method, originalRequest : Object) : Boolean = {
    if(originalRequest == null || !originalRequest.isInstanceOf[JavaMap[_,_]]){
      return false
    }
    
    val paramMap = originalRequest.asInstanceOf[JavaMap[_,_]]
    
    val fieldMap = Map[String, Class[_]]()
    for((paramKey , paramValue)  <- paramMap.asScala){
      val paramType = getParamType(paramValue)
      fieldMap += (paramKey.toString() -> paramType)
    }
    
    containsParam(method, fieldMap)
  }

  private def getParamType(paramValue: Any) : Class[_] = {
    if (paramValue == null) classOf[Object] else paramValue.getClass
  }

  private def isNatureMatch(method : Method, originalRequest : Object) : Boolean = {
    val paramCount = method.getParameterCount
    
    if(paramCount == 0){
      return true;
    }
    
    if(originalRequest == null){
      return false
    }
    
    if(paramCount == 1){
      val requiredParamType = method.getParameterTypes.apply(0)
      if(requiredParamType.isInstance(originalRequest)){
        return true
      }
    }
    
    if(!originalRequest.getClass.isArray()){
      return false
    }
    
    val paramArray = originalRequest.asInstanceOf[Array[_]]
    val requestParamCount = paramArray.length
    if(paramCount != requestParamCount){
      return false
    }
    
    for(i <- 0 to paramCount - 1){
      val requiredParamType = method.getParameterTypes.apply(i)
      val requestParamType = paramArray.apply(i).getClass()
      if(!requiredParamType.equals(requestParamType)){
        return false
      }
    }
    
    true
  }
  
  private def containsParamAsObject(method : Method, originalRequest : Object): Boolean = {
    if(originalRequest == null){
      return false
    }
    
    val fieldArray = originalRequest.getClass.getDeclaredFields
    val fieldMap = Map[String, Class[_]]()
    for(field <- fieldArray){
      fieldMap += (field.getName -> field.getType)
    }
    
    containsParam(method, fieldMap)
  }

  private def containsParam(method: Method, fieldMap: Map[String,Class[_]]) : Boolean = {
    for(param <- method.getParameters) {
      val paramType = param.getType
      val paramName = param.getName
      if(!fieldMap.contains(paramName)){
        return false
      }
      
      if(!fieldMap.get(paramName).equals(paramType)){
        return false
      }
    }
    
    return true
  }
  
  def main(args: Array[String]): Unit = {
    val objArray = Array[Object]()
    println(objArray.getClass.isArray())
  }
}